﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;

namespace System.Windows.Media.Imaging
{
    public static class ResizeBicubicEx
    {
        //class ResizeWorkItem
        //{
        //    public WriteableBitmap Source { get; set; }
        //    public int NewWidth { get; set; }
        //    public int NewHeight { get; set; }
        //    public ResizeResult Callback { get; set; }
        //}

        //public delegate void ResizeResult(WriteableBitmap resized);

        //public static void ResizeBicubicAsync(this WriteableBitmap src, int newWidth, int newHeight, ResizeResult callback)
        //{
        //    System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(DoResizeBicubic), new ResizeWorkItem()
        //        {
        //            Source = src,
        //            NewWidth = newWidth,
        //            NewHeight = newHeight,
        //            Callback = callback
        //        });
        //}

        //static void DoResizeBicubic(object state)
        //{
        //    var workItem = (ResizeWorkItem)state;
        //    var resized = ResizeBicubic(workItem.Source, workItem.NewWidth, workItem.NewHeight);
        //    workItem.Callback(resized);
        //}

        /// <summary>
        /// Bicubic resize algorithm suitable for use in Silverlight
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dest"></param>
        /// <remarks>Original Source For This Function - http://www.codeproject.com/KB/recipes/aforge.aspx
        /// Being a part of AForge, it is licensed under the LGPL. See http://www.aforgenet.com/framework/license.html
        /// </remarks>
        public static WriteableBitmap ResizeBicubic(this WriteableBitmap src, int newWidth, int newHeight)
        {
            var dest = new WriteableBitmap(newWidth, newHeight);

            int height = src.PixelHeight;
            int width = src.PixelWidth;

            double xFactor = (double)width / newWidth;
            double yFactor = (double)height / newHeight;

            // coordinates of source points and cooefficiens
            double ox, oy, dx, dy, k1, k2;
            int ox1, oy1, ox2, oy2;
            // destination pixel values
            double a, r, g, b;
            // width and height decreased by 1
            int ymax = height - 1;
            int xmax = width - 1;

            // RGB
            for (int y = 0; y < newHeight; y++)
            {
                // Y coordinates
                oy = (double)y * yFactor - 0.5f;
                oy1 = (int)oy;
                dy = oy - (double)oy1;

                for (int x = 0; x < newWidth; x++)
                {
                    // X coordinates
                    ox = (double)x * xFactor - 0.5f;
                    ox1 = (int)ox;
                    dx = ox - (double)ox1;

                    // initial pixel value
                    a = r = g = b = 0;

                    for (int n = -1; n < 3; n++)
                    {
                        // get Y cooefficient
                        k1 = BiCubicKernel(dy - (double)n);

                        oy2 = oy1 + n;
                        if (oy2 < 0)
                            oy2 = 0;
                        if (oy2 > ymax)
                            oy2 = ymax;

                        for (int m = -1; m < 3; m++)
                        {
                            // get X cooefficient
                            k2 = k1 * BiCubicKernel((double)m - dx);

                            ox2 = ox1 + m;
                            if (ox2 < 0)
                                ox2 = 0;
                            if (ox2 > xmax)
                                ox2 = xmax;

                            // get pixel of original image
                            // p = src + oy2 * srcStride + ox2 * 3;
                            int srcPixel = src.Pixels[ox2 + (width * oy2)];

                            a += k2 * ((srcPixel >> 24) & 0xff);
                            r += k2 * ((srcPixel >> 16) & 0xff);
                            g += k2 * ((srcPixel >> 8) & 0xff);
                            b += k2 * (srcPixel & 0xff);
                        }
                    }

                    dest.Pixels[x + (newWidth * y)] = (int)((byte)a << 24 | ((byte)r << 16) | ((byte)g << 8) | (byte)b);

                }

            }

            return dest;
        }

        private static double BiCubicKernel(double x)
        {
            if (x > 2.0)
                return 0.0;

            double a, b, c, d;
            double xm1 = x - 1.0;
            double xp1 = x + 1.0;
            double xp2 = x + 2.0;

            a = (xp2 <= 0.0) ? 0.0 : xp2 * xp2 * xp2;
            b = (xp1 <= 0.0) ? 0.0 : xp1 * xp1 * xp1;
            c = (x <= 0.0) ? 0.0 : x * x * x;
            d = (xm1 <= 0.0) ? 0.0 : xm1 * xm1 * xm1;

            return (0.16666666666666666667 * (a - (4.0 * b) + (6.0 * c) - (4.0 * d)));
        }
    }
}
